package com.lian.chatroom.protocol;

import com.lian.chatroom.config.Config;
import com.lian.chatroom.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

/**
 * 必须和 LengthFieldBasedFrameDecoder 一起使用，确保接到的 ByteBuf 消息是完整的
 * 消息编解码
 * 出栈：ByteBuf格式数据 转换为 字符串等其他格式 解码
 * 入栈：字符串等其他格式 转换为  ByteBuf格式数据 编码
 */
@Slf4j
@ChannelHandler.Sharable
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {
        //用通道分配一个缓存区
        ByteBuf out = ctx.alloc().buffer();
        //1. 4 字节的魔数，就是服务端和客户端约定好的暗号，例如：天王盖地虎 宝塔镇魔妖
        out.writeBytes(new byte[]{1, 2, 3, 4});
        // 2. 1 字节的版本,
        out.writeByte(1);
        // 3. 1 字节的序列化方式 jdk 0 , json 1
        //out.writeByte(0); //写死的方式
        //3.1 采用配置类灵活选择序列化方式，返回此枚举常量的序号，如果序列化方式是jdk就会填写0，如果是json就会填写1
        out.writeByte(Config.getSerializerAlgorithm().ordinal());
        // 4. 1 字节的指令类型
        out.writeByte(msg.getMessageType());
        // 5. 4 个字节
        out.writeInt(msg.getSequenceId());
        // 无意义，对齐填充
        out.writeByte(0xff);
        // 6. 获取内容的字节数组
//        ByteArrayOutputStream bos = new ByteArrayOutputStream();
//        ObjectOutputStream oos = new ObjectOutputStream(bos);
//        oos.writeObject(msg);
//        byte[] bytes = bos.toByteArray();

        //6.1、采用jdk方式序列化，将java对象转为字节数组
        //byte[] bytes = Serializer.Algorithm.java.serializer(msg);

        //6.2、采用json方式序列化
        //byte[] bytes = Serializer.Algorithm.json.serializer(msg);

        //6.3、采用配置类形式，来灵活选择使用哪种 序列化方式
        byte[] bytes = Config.getSerializerAlgorithm().serializer(msg);

        // 7. 长度
        out.writeInt(bytes.length);
        // 8. 将字节数组写入到缓存区
        out.writeBytes(bytes);
        outList.add(out);

    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int magicNum = in.readInt();
        byte version = in.readByte();
        //从缓存区中读取到编码时用的哪种序列化算法类型，是jdk or json
        //返回 0 or 1, 0代表jdk序列化方式，1代表json序列化方式
        byte serializerAlgorithm = in.readByte();
        //消息类型，0,1,2，。。。
        byte messageType = in.readByte();
        int sequenceId = in.readInt();
        //从缓存区读取字节数组数据
        in.readByte();
        //获取缓存区内字节数组的大小
        int length = in.readInt();
        //生成和缓冲区数据大小相同的byte数组，将缓存区内数据 封装到 byte数组
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length);
//        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
//        Message message = (Message) ois.readObject();

        //采用jdk方式反序列化，将byte数组转为Message对象
        //Message message = Serializer.Algorithm.java.deSerializer(bytes, Message.class);

        //采用json方式反序列化
        //Message message = Serializer.Algorithm.json.deSerializer(bytes, Message.getMessageClass(messageType));

        //采用配置类灵活选择使用哪种序列化方式进行解码
        //values返回全部序列化方式，下标为0就是jdk方式，下标为1就是json方式，必须和序列化的编解码方式相同
        //Serializer.Algorithm.values()[serializerAlgorithm] 找到反序列化方式算法，是jdk还是json
        //Message.getMessageClass(messageType) 确定具体消息类型
        Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializerAlgorithm];
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        Message message = algorithm.deSerializer(bytes, messageClass);

//        log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerAlgorithm, messageType, sequenceId, length);
//        log.debug("{}", message);
        out.add(message);
    }
}
